package com.tsfyun.scm.service.impl.third;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.ImmutableMap;
import com.tsfyun.common.base.config.properties.NoticeProperties;
import com.tsfyun.common.base.dto.ShortMessageDTO;
import com.tsfyun.common.base.dto.SmsNoticeMessageDTO;
import com.tsfyun.common.base.enums.MessageNodeEnum;
import com.tsfyun.common.base.enums.shortmsg.ShortMessagePlatformEnum;
import com.tsfyun.common.base.exception.ServiceException;
import com.tsfyun.common.base.help.DingTalkNoticeUtil;
import com.tsfyun.common.base.support.*;
import com.tsfyun.common.base.util.*;
import com.tsfyun.scm.entity.third.LogSms;
import com.tsfyun.scm.service.third.AbstractMessageService;
import com.tsfyun.scm.service.third.ILogSmsService;
import com.tsfyun.scm.service.third.IMessageService;
import com.tsfyun.scm.service.third.IShortMessageService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

@RefreshScope
@Service
@Slf4j
public class ShortMessageServiceImpl implements IShortMessageService {
    //短信发送平台
    @Value("${sms.platform:aliyun}")
    private String smsPlatform;
    @Autowired
    private ILogSmsService logSmsService;
    @Autowired
    private NoticeProperties noticeProperties;
    @Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
    public static Map<ShortMessagePlatformEnum, IMessageService> messageServiceMap;
    @Autowired
    private Map<String,IMessageService> springMessageServiceMap;

    @PostConstruct
    public void init() {
        messageServiceMap = new ConcurrentHashMap<>();
        springMessageServiceMap.forEach((key,value) -> messageServiceMap.put(value.getPlatform(),value));
    }

    //短信平台及模板实例映射
    private Map<ShortMessagePlatformEnum, MessageOperation> messageOperationMap = ImmutableMap.<ShortMessagePlatformEnum,MessageOperation>builder()
            .put(ShortMessagePlatformEnum.ALIYUN,AliyunMessageOperation.getInstance())
            .put(ShortMessagePlatformEnum.TECENT, TencentMessageOperation.getInstance())
            .put(ShortMessagePlatformEnum.WELINK, WelinkMessageOperation.getInstance())
            .build();

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void sendVerificationCode(ShortMessageDTO dto, MessageNodeEnum messageNode, String ip) {
        ShortMessagePlatformEnum shortMessagePlatformEnum = ShortMessagePlatformEnum.of(smsPlatform);
        Optional.ofNullable(shortMessagePlatformEnum).orElseThrow(()->new ServiceException("配置的短信平台不支持"));
        MessageOperation messageOperation = messageOperationMap.get(shortMessagePlatformEnum);
        OperationEnum messageTemplate = messageOperation.getMessageTemplate().get(messageNode);
        if(Objects.isNull(messageTemplate)) {
            log.warn("短信平台【{}】未找到短信节点【{}】的配置，不发送短信平台",shortMessagePlatformEnum.getName(),messageNode.getName());
            return;
        }
        String phoneNo = dto.getPhoneNo();
        TsfPreconditions.checkArgument(VerificationUtils.isMobile(phoneNo),new ServiceException("无效手机号码"));
        TsfPreconditions.checkArgument(StringUtils.isNotEmpty(ip),new ServiceException("您的网络环境不安全"));
        //统计手机号一分钟时候有重复发送
        Integer count = logSmsService.countOneMinute(phoneNo,messageNode);
        TsfPreconditions.checkArgument(count==0,new ServiceException("发送频率过高"));
        //同一个IP一分钟内不能超过20条
        count = logSmsService.countByIpAndMinute(ip,1);
        TsfPreconditions.checkArgument(count<=20,new ServiceException("发送频率过高"));
        //5分钟内保持验证码不变
        String code = logSmsService.selectCodeNew(phoneNo,messageNode,5);
        if(StringUtils.isEmpty(code)){
            code = StringUtils.randomStr4();
        }
        log.info("正在使用【{}】发送验证短信，手机号码【{}】",shortMessagePlatformEnum.getName(),phoneNo);
        log.info("{}：{}：验证码：【{}】",ip,messageNode.getName(),code);
        String messageContent = messageTemplate.getName().replaceAll("\\{[^{}]*\\}",code);

        LogSms logSms = new LogSms();
        logSms.setPhoneNo(phoneNo);
        logSms.setNode(messageNode.getCode());
        logSms.setIp(ip);
        logSms.setCode(code);
        logSms.setContent(messageContent);
        logSms.setBackMessage("等待发送");
        logSmsService.saveNonNull(logSms);
        IMessageService messageService = messageServiceMap.get(shortMessagePlatformEnum);
        messageService.sendVerificationCode(logSms);
    }

    public void checkVerificationCode(String phoneNo, MessageNodeEnum messageNode, String code){
        String scode = logSmsService.selectCodeNew(phoneNo,messageNode,5);
        if(StringUtils.isEmpty(scode)){
            throw new ServiceException("短信验证码已失效,请重新获取");
        }
        if(!scode.equalsIgnoreCase(code)){
            throw new ServiceException("短信验证码输入错误");
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void sendNoticeMessage(SmsNoticeMessageDTO dto) {
        String phoneNo = dto.getPhoneNo();
        if(StringUtils.isEmpty(phoneNo)) {
            log.error("手机号码为空，不发送短信");
            return;
        }
        if(!VerificationUtils.isMobile(phoneNo)) {
            log.error("手机号码【{}】格式错误",phoneNo);
            DingTalkNoticeUtil.send2DingDingOtherException("发送短信通知异常",String.format("发送短信通知异常，手机号码【%s】非法",phoneNo),null,noticeProperties.getDingdingUrl());
        } else {
            String ip = TypeUtils.castToString(RequestUtils.getIp(),"127.0.0.1");
            //获取参数个数
            LinkedHashMap<String,String> params = dto.getParams();
            ShortMessagePlatformEnum shortMessagePlatformEnum = ShortMessagePlatformEnum.of(smsPlatform);
            Optional.ofNullable(shortMessagePlatformEnum).orElseThrow(()->new ServiceException("配置的短信平台不支持"));
            MessageOperation messageOperation = messageOperationMap.get(shortMessagePlatformEnum);
            OperationEnum messageTemplate = messageOperation.getMessageTemplate().get(dto.getMessageNodeEnum());
            if(Objects.isNull(messageTemplate)) {
                log.warn("短信平台【{}】未找到短信节点【{}】的配置，不发送短信平台",shortMessagePlatformEnum.getName(),dto.getMessageNodeEnum().getName());
                return;
            }
            String smsTemplateContent = messageTemplate.getName();
            LinkedHashSet<String> templateParams = RegUtil.getMessageParams(smsTemplateContent);
            //检查短信模板参数是否传值
            final String[] messageContent = {messageTemplate.getName()};
            //参数统一调整成按字符分隔，防止json序列化出现顺序问题
            Map<String,String> paramMap = new LinkedHashMap<>();
            templateParams.stream().forEach(paramKey->{
                String paramValue = params.get(paramKey);
                if(StringUtils.isEmpty(paramValue)) {
                    log.error("手机号码【{}】短信模板【{}】参数【{}】，值为空",phoneNo,messageTemplate.getCode(),paramKey);
                    throw new ServiceException("短信参数错误");
                }
                paramMap.put(paramKey,StringUtils.null2EmptyWithTrim(paramValue));
                messageContent[0] = messageContent[0].replaceAll("\\{" + paramKey + "\\}",paramValue);
            });
            log.info("正在使用【{}】发送验证短信，手机号码【{}】",shortMessagePlatformEnum.getName(),phoneNo);
            AbstractMessageService messageService = (AbstractMessageService)messageServiceMap.get(shortMessagePlatformEnum);
            //异步写入不影响主业务流程
            threadPoolTaskExecutor.execute(()->{
                LogSms logSms = new LogSms();
                logSms.setPhoneNo(phoneNo);
                logSms.setNode(dto.getMessageNodeEnum().getCode());
                logSms.setIp(ip);
                //通知类短信验证码暂定为-1
                logSms.setCode("-1");
                String smsContent = messageContent[0];
                logSms.setContent(smsContent);
                logSms.setBackMessage("等待发送");
                logSmsService.saveNonNull(logSms);
                messageService.sendNoticeMessageWithMQ(shortMessagePlatformEnum,logSms,messageTemplate.getCode(), JSON.toJSONString(paramMap));
            });
        }
    }

}
